
///
/// This is the implementation of the Server class
///

#include <iostream>
#include <netdb.h>
#include <arpa/inet.h>
#include <cstring>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstdio>
#include <cassert>
#include "Server.h"
#include <algorithm>

Server* Server::mServer = NULL;

int Server::Initialize(const char *ip, const char *portNo)
{
	struct addrinfo temp, *res;
	memset(&temp, 0, sizeof(temp));		temp.ai_family = AF_UNSPEC;		temp.ai_socktype = SOCKET_TYPE;
	
	int retVal;

	if((retVal = getaddrinfo(ip, /*TFTP_PORT_STR*/portNo, &temp, &res)) != 0)
	{
		std::cout << "getaddrinfo() error: " << gai_strerror(retVal) << std::endl;
		exit(1);
	}


	std::cout << "Starting the server..." << std::endl;
	std::cout << "IP Address: " << ip << std::endl;
	std::cout << "Port Number: " << /*TFTP_PORT_STR*/portNo << std::endl;

	//create server main(connection request handler) socket
	mMainFD = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if(mMainFD == -1)
	{
		perror("socket() error: ");
		exit(2);
	}

	//need to reuse the socket
	int yes = 1;
	if(setsockopt(mMainFD, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
	{
		perror("setsockopt() error: ");
		exit(2);
	}

	if(bind(mMainFD, res->ai_addr, res->ai_addrlen) == -1)
	{
		perror("bind() error: ");
		close(mMainFD);
		exit(2);
	}

	freeaddrinfo(res);

	std::cout << "Server started successfully!" << std::endl;

	struct sockaddr_in tempaddr;
	unsigned int templen = sizeof(struct sockaddr);
	getsockname(mMainFD, (struct sockaddr*)&tempaddr, &templen);
	std::cout << "Server listens to port: " << ntohs(tempaddr.sin_port) << std::endl;


	return 0;
}

int Server::Execute()
{

	struct sockaddr_storage clientAddr;
	socklen_t clientAddrSize = sizeof(clientAddr);

	//initialize FD sets for select()
	fd_set readFDS;
	FD_ZERO(&readFDS);
	FD_ZERO(&mConnectedFDS);
	
	FD_SET(mMainFD, &mConnectedFDS);
	FD_SET(STDIN, &mConnectedFDS); //for command line control of server
	mMaxFD = std::max(STDIN, mMainFD);
	//clientFD always beins from 4, why????????????????
	//TODO fix this problem later
	
	//mMaxFD = 10;


	struct timeval tv;
	tv.tv_sec = 300;	tv.tv_usec = 0; // 5 min timeout

	int retVal, numBytes;

	while(1)
	{
		readFDS = mConnectedFDS;
		retVal = select(mMaxFD+1, &readFDS, NULL, NULL, &tv);
		if(retVal == -1)
		{
			perror("select() error: ");
			return 3;
		}
		else if(retVal == 0)
		{
			std::cout << "Timeout occured. Nobody's interested in chatting! Exiting..." << std::endl;
			return 0;
		}

		for(int i = 0; i <= mMaxFD; ++i)
		{
			if(FD_ISSET(i, &readFDS))
			{
				if(i == STDIN)
				{
					//TODO: handle std input commands
				}
				else if(i == mMainFD)
				{
					//seems like there's a new request
					if((numBytes = recvfrom(mMainFD, mMsgBuf, MSG_BUF_SIZE, 0, 
						(struct sockaddr*)&clientAddr, &clientAddrSize)) == -1)
					{
						perror("recvfrom() error: ");
						exit(1);
					}
					mMsgBuf[numBytes] = 0;
					std::cout << "Server received the msg:\n" << mMsgBuf
									<< std::endl;
					
					uint16_t temp, opCode;
					memcpy((char*)&temp, mMsgBuf, 2);
					opCode = ntohs(temp);

					assert(opCode == 1); //only RRQ supported

					std::string fileName(mMsgBuf+2);

					//thats it I guess, not to worry about the mode
					
					int clientFD = socket(PF_INET, SOCKET_TYPE, 0);
					std::cout<<"clientFD is "<<clientFD<<std::endl;
					std::cout<<"MaxFD is "<<mMaxFD<<std::endl;
					FD_SET(clientFD,&readFDS);					
					if(clientFD > mMaxFD)
						mMaxFD = clientFD;					
					Handler handler(clientFD, fileName, clientAddr, 
										clientAddrSize);
					
					std::cout<<"first packet sent"<<std::endl;
					mClientMap[clientFD] = handler;
					mClientMap[clientFD].initial(fileName, clientAddr, clientAddrSize);
					/*
					std::cout<<"testing"<<std::endl;	
					recvfrom(clientFD, mMsgBuf, MSG_BUF_SIZE, 0,
						(struct sockaddr*)&clientAddr, &clientAddrSize);
					mClientMap[clientFD].HandleMessage(mMsgBuf, numBytes);				
					std::cout<<"testing end"<<std::endl;	
					*/
				}
				else
				{
					//seems like an ACK arrived
					std::cout<<"fd number is "<<i<<std::endl;
					std::cout<<"receive response"<<std::endl;
					if((numBytes = recvfrom(i, mMsgBuf, MSG_BUF_SIZE, 0, 
						(struct sockaddr*)&clientAddr, &clientAddrSize)) == -1)
					{
						perror("recvfrom() error: ");
						exit(1);
					}
					mClientMap[i].HandleMessage(mMsgBuf, numBytes);
				}
			}
		}
	}
	
	close(mMainFD);
	return 0;
}
#if 0
//////////////////////
//// PRIVATE /////////
//////////////////////

void Server::KillClient(int sockFD)
{
	//close the socket
	close(sockFD);

	//remove socket from fd set
	FD_CLR(sockFD, &mConnectedFDS);
	if(sockFD == mMaxFD)
	{
		UpdateMaxFd();
	}

	//remove socket from map
	mClientMap.erase(sockFD);
}

void Server::UpdateMaxFd()
{
	for(int i = mMaxFD-1; i > 0; --i)
	{
		if(FD_ISSET(i, &mConnectedFDS))
		{
			mMaxFD = i;
			return;
		}
	}
}

int Server::SendOnlineToOthers(int sockFD)
{
	const std::string& userName = mClientMap[sockFD].first;	
	const unsigned int userNameLength = userName.length();

	StructAttribute attribute;
	attribute.type = ATTR_TYPE_USERNAME;
	attribute.length = userNameLength + ATTR_SIZE;
			
	StructHeader header;
	header.version = PROTOCOL_VERSION;
	header.type = HEADER_TYPE_ONLINE;
	header.length = attribute.length + HEADER_SIZE;
	
	char* dest = &mMsgBuf[0];
	
	uint16_t host, nw;

	memcpy((char*)&host, (char*)&header, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&header))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;	
	
	memcpy((char*)&host, (char*)&attribute, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&attribute))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;		

	memcpy(dest, userName.c_str(), userNameLength);
	dest += userNameLength;

	//sanity check
	assert((dest - mMsgBuf) == header.length);
	
	for(int i = 0; i <= mMaxFD; i++)
	{
		if (FD_ISSET(i, &mConnectedFDS))
		{
			if((i != mMainFD) && (i != sockFD) && (i != STDIN))
			{
				if(send(i, mMsgBuf, header.length, 0) == -1)
				{
					std::cout << "Failed to send ONLINE to: " << mClientMap[i].first << std::endl;
					return 1;
				}
			}
		}
	}
	return 0;

}

int Server::SendNak(int sockFD, NAK_TYPE nakType)
{
	static const char* userNameNak = "Username already exists! Use another name and retry.";
	static const char* clientCountNak = "Max Client Count reached! Try again later.";

	static const unsigned int userNameNakLength = strlen(userNameNak);
	static const unsigned int clientCountNakLength = strlen(clientCountNak);

	const char* reason;
	unsigned int reasonLength;
	if(nakType == NAK_TYPE_USERNAME)
	{
		reason = userNameNak;
		reasonLength = userNameNakLength;
	}
	else if(nakType == NAK_TYPE_CLIENT_COUNT)
	{
		reason = clientCountNak;
		reasonLength = clientCountNakLength;
	}
	else
		assert(false);


	StructAttribute attribute;
	attribute.type = ATTR_TYPE_REASON;
	attribute.length = reasonLength + ATTR_SIZE;

	StructHeader header;
	header.version = PROTOCOL_VERSION;
	header.type = HEADER_TYPE_NAK;
	header.length = attribute.length + HEADER_SIZE;

	char* dest = &mMsgBuf[0];

	uint16_t host, nw;

	memcpy((char*)&host, (char*)&header, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&header))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;

	memcpy((char*)&host, (char*)&attribute, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&attribute))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;	

	memcpy(dest, reason, reasonLength);
	dest += reasonLength;

	//sanity check
	assert((dest - mMsgBuf) == header.length);
	
	return send(sockFD, mMsgBuf, header.length, 0);
}

int Server::SendAck(int sockFD)
{
	unsigned int clientCount = 0;
	
	uint16_t host, nw;

	StructAttribute attribute;
	attribute.type = ATTR_TYPE_USERNAME;

	//leave room for header and 2-byte client count attr
	char* dest = &mMsgBuf[0] + HEADER_SIZE + ATTR_SIZE + CLIENT_COUNT_BYTES; 
	unsigned int nameLength;

	//iterate over current client connections
	for(SocketToHandlerMapping::iterator tempIter = mClientMap.begin();
			tempIter != mClientMap.end();
			++tempIter)
	{
		std::string& name = tempIter->second.first;
		if(name.empty() == false) //client has JOINed //sockFD's name is still empty
		{
			++clientCount;

			nameLength = name.length();
			attribute.length = nameLength + ATTR_SIZE;
			
			memcpy((char*)&host, (char*)&attribute, 2);
			nw = htons(host);
			memcpy(dest, (const char*)&nw, 2);
			dest += 2;
			memcpy((char*)&host, ((char*)(&attribute))+2, 2);
			nw = htons(host);
			memcpy(dest, (const char*)&nw, 2);
			dest += 2;	
			
			memcpy(dest, name.c_str(), nameLength);
			dest += nameLength;
		}
	}

	std::cout << "Client Count in SendAck is: " << clientCount << std::endl;

	StructHeader header;
	header.version = PROTOCOL_VERSION;
	header.type = HEADER_TYPE_ACK;
	header.length = dest - mMsgBuf;
	
	memcpy((char*)&host, (char*)&header, 2);
	nw = htons(host);
	memcpy(&mMsgBuf[0], (const char*)&nw, 2);
	memcpy((char*)&host, ((char*)(&header))+2, 2);
	nw = htons(host);
	memcpy(&mMsgBuf[0]+2, (const char*)&nw, 2);
	
	attribute.type = ATTR_TYPE_CLIENT_COUNT;
	attribute.length = ATTR_SIZE + CLIENT_COUNT_BYTES;
	
	memcpy((char*)&host, (char*)&attribute, 2);
	nw = htons(host);
	memcpy(&mMsgBuf[0] + HEADER_SIZE, (const char*)&nw, 2);

	memcpy((char*)&host, ((char*)(&attribute))+2, 2);
	nw = htons(host);
	memcpy(&mMsgBuf[0] + HEADER_SIZE + 2, (const char*)&nw, 2);
	
	
	uint16_t temps;
	memcpy((char*)&temps, (char*)&clientCount, CLIENT_COUNT_BYTES);
	uint16_t clientCountToSend = htons(temps);
	
	memcpy(&mMsgBuf[0] + HEADER_SIZE + ATTR_SIZE, (const char*)&clientCountToSend, CLIENT_COUNT_BYTES);

	return send(sockFD, mMsgBuf, header.length, 0);
}

///////////////////////
///// HANDLERS ////////
///////////////////////

///
/// This is probably the most expensive and heavy command
/// The server should check for client count and username to decide acceptability
/// It should send ACK to joinee with list of active usernames 
///	and ONLINE to others with username if accepted
/// It should send NAK to joinee with reason and close socket and clear map and fd_set if denied
///

int Server::HandleJoinCommand(int sockFD, std::string& userName)
{
	assert(userName.empty() == false); //no jokes, okay!!!

	//change userName to lower case for future ease
	std::transform(userName.begin(), userName.end(), userName.begin(), ::tolower);

	unsigned int clientCount = 0;

	//iterate over current client connections
	for(SocketToHandlerMapping::iterator tempIter = mClientMap.begin();
			tempIter != mClientMap.end();
			++tempIter)
	{
		std::string& name = tempIter->second.first;
		if(name.empty() == false) //client has JOINed
		{
			++clientCount;
			if(name.compare(userName) == 0)
			{
				//username already exists, deny request
				SendNak(sockFD, NAK_TYPE_USERNAME);
				KillClient(sockFD);
				return 1;
			}
		}
	}
	
	std::cout << "Client Count in HandleJoinCommand is: " << clientCount << std::endl;

	if(clientCount == mMaxClientCount)
	{
		//max client count reached, deny request
		SendNak(sockFD, NAK_TYPE_CLIENT_COUNT);
		KillClient(sockFD);
		return 2;
	}

	//all is well, accept request
	SendAck(sockFD);

	//
	// This setting of username should be after Ack is sent, else
	// the Ack will be sent to self
	// It should be before sending ONLINE to others, else
	// the username string is empty
	//
	mClientMap[sockFD].first = userName; //not empty anymore, i.e. JOINed

	SendOnlineToOthers(sockFD);

	return 0;
}

int Server::HandleLeaveCommand(int sockFD)
{
	const std::string& userName = mClientMap[sockFD].first;	
	const unsigned int userNameLength = userName.length();

	StructAttribute attribute;
	attribute.type = ATTR_TYPE_USERNAME;
	attribute.length = userNameLength + ATTR_SIZE;
			
	StructHeader header;
	header.version = PROTOCOL_VERSION;
	header.type = HEADER_TYPE_OFFLINE;
	header.length = attribute.length + HEADER_SIZE;

	char* dest = &mMsgBuf[0];

	uint16_t host, nw;

	memcpy((char*)&host, (char*)&header, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&header))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;

	memcpy((char*)&host, (char*)&attribute, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&attribute))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;	

	memcpy(dest, userName.c_str(), userNameLength);
	dest += userNameLength;

	//sanity check
	assert((dest - mMsgBuf) == header.length);
	
	//send OFFLINE message to others
	for(int i = 0; i <= mMaxFD; i++)
	{
		if (FD_ISSET(i, &mConnectedFDS))
		{
			if((i != mMainFD) && (i != sockFD) && (i != STDIN))
			{
				if(send(i, mMsgBuf, header.length, 0) == -1)
				{
					std::cout << "Failed to send OFFLINE to: " << mClientMap[i].first << std::endl;
					return 1;
				}
			}
		}
	}

	KillClient(sockFD);

	return 0;
}

int Server::HandleSendCommand(int sockFD, const std::string& message)
{
	const std::string& userName = mClientMap[sockFD].first;
	const unsigned int userNameLength = userName.length();

	StructAttribute attrUsername;
	attrUsername.type = ATTR_TYPE_USERNAME;
	attrUsername.length = userNameLength + ATTR_SIZE;

	StructAttribute attrMessage;
	attrMessage.type = ATTR_TYPE_MESSAGE;
	attrMessage.length = message.length() + ATTR_SIZE;

	StructHeader header;
	header.version = PROTOCOL_VERSION;
	header.type = HEADER_TYPE_FWD;
	header.length = attrUsername.length + attrMessage.length + HEADER_SIZE;
	
	char* dest = &mMsgBuf[0];

	uint16_t host, nw;

	memcpy((char*)&host, (char*)&header, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&header))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;

	memcpy((char*)&host, (char*)&attrUsername, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&attrUsername))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;

	memcpy(dest, userName.c_str(), userNameLength);
	dest += userNameLength;

	memcpy((char*)&host, (char*)&attrMessage, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;
	memcpy((char*)&host, ((char*)(&attrMessage))+2, 2);
	nw = htons(host);
	memcpy(dest, (const char*)&nw, 2);
	dest += 2;

	memcpy(dest, message.c_str(), message.length());
	dest += message.length();

	//sanity check
	assert((dest - mMsgBuf) == header.length);

	for(int j = 0; j <= mMaxFD; ++j)
	{
		if(FD_ISSET(j, &mConnectedFDS))
		{
			if((j != mMainFD) && (j != sockFD) && (j != STDIN))
			{
				if(send(j, mMsgBuf, header.length, 0) == -1)
				{
					std::cout << "FWD failed to client: " << mClientMap[j].first.c_str() << std::endl;
				}
			}
		}
	}
	return 0;
}
#endif
